import { EOChild } from '../mve/childrenBuilder'
import { modelChildren } from '../mve/modelChildren'
import { mve } from '../mve/util'
import { dom } from './index'
import { FlatTree } from './flatTree'
/**
 * 创建视图
 * @param me 
 * @param router 
 * @param createRoutes 
 * @param other 
 * @returns 
 */
export function routerView<T>(
	me: mve.LifeModel, router: Router<T>, createRoutes: { [key in keyof T]: CreateSubRouter<T[key]> }, other?: {
		notFoundView?: Router<any>
	}
) {
	other = other || {}
	const routes: { [key: string]: Router<T> } = {}
	mb.Object.forEach(createRoutes, function (v, k) {
		const fun = v as CreateSubRouter<any>
		routes[k] = fun(router.parentPath.concat(k))
	})

	const notFoundView = other.notFoundView || default404FactoryRouter(router.parentPath.concat("404"))
	const currentRoute = mve.arrayModelOf<Router<T>>([])
	function clear() {
		if (currentRoute.size() > 0) {
			currentRoute.remove(0)
		}
	}
	me.WatchAfter(
		function () {
			return router.param()
		},
		function (p) {
			if (p) {
				const { path: [first, ...rest], query } = p
				const currentR = currentRoute.get(0, true)
				if (first) {
					//有路径
					const changeR = routes[first] || notFoundView
					//并且能找到路由
					if (currentR == changeR) {
						//并未发生改变
						currentR.param({
							path: rest, query
						})
					} else {
						//发生了改变，替换当前route
						clear()
						currentRoute.push(changeR as any)
						changeR.param({
							path: rest, query
						})
					}
				} else {
					clear()
				}
			} else {
				clear()
			}
		}
	)
	return modelChildren(currentRoute, function (me, row, i) {
		return row.render(me)
	})
}

export interface QueryParam {
	[key: string]: string | string[]
}
export interface RouterParam {
	path: string[]
	query: QueryParam
}
export type CreateSubRouter<T> = (parentPath: string[]) => Router<T>
export class Router<T>{
	readonly param = mve.valueOf<RouterParam>({ path: [], query: {} })
	constructor(
		public readonly parentPath: string[],
		private fun: (me: mve.LifeModel, router: Router<T>) => EOChild<Node>
	) { }

	current(): keyof T {
		return this.param().path[0] as keyof T
	}
	render(me: mve.LifeModel) {
		return this.fun(me, this)
	}
	/**
	 * 生成合适的路由
	 * @param path 
	 * @param query 
	 * @returns 
	 */
	href<K extends keyof FlatRouter<T>>(path: K, query: (FlatRouter<T>[K] extends QueryWrapper ? FlatRouter<T>[K]['value'] : never)) {
		return this.parentPath.concat(path as string).join('/') + queryToString(query as any)
	}
	/**
	 * 中能在自己的下游路径漫游
	 * @param path 
	 * @param query 
	 */
	go<K extends keyof FlatRouter<T>>(path: K, query: (FlatRouter<T>[K] extends QueryWrapper ? FlatRouter<T>[K]['value'] : never)) {
		location.hash = this.href(path, query)
	}
}

export function queryToString(q?: QueryParam) {
	if (q) {
		const vs: string[] = []
		mb.Object.forEach(q, function (v, k) {
			if (v instanceof Array) {
				for (const i of v) {
					vs.push(`${k}=${i}`)
				}
			} else {
				vs.push(`${k}=${v}`)
			}
		})
		if (vs.length > 0) {
			return '?' + vs.join('&')
		}
	}
	return ''
}

export function createRouter<T>(
	fun: (me: mve.LifeModel, router: Router<T>
	) => EOChild<Node>): CreateSubRouter<T> {
	return (parentPath) => {
		return new Router<T>(parentPath, fun)
	}
}

//////////////////////////////////////////////////////

export function hashToParam(hash: string): RouterParam {
	const query = {}
	const path = []
	if (hash) {
		const [pathStr, queryStr] = hash.split("?")
		pathStr.slice(1).split("/").forEach(v => {
			if (v) {
				path.push(v)
			}
		})
		if (queryStr) {
			queryStr.split("&").forEach(kv => {
				const [k, v] = kv.split("=")
				const oldV = query[k]
				if (oldV) {
					if (Array.isArray(oldV)) {
						oldV.push(v)
					} else {
						query[k] = [oldV, v]
					}
				} else {
					query[k] = v
				}
			})
		}
	}
	if (path.length == 0) {
		path.push("index")
	}
	return { path, query }
}

export function locationHashChange(route: Router<any>) {
	const hash = location.hash
	route.param(hashToParam(hash))
}

export function locationHashBind(route: Router<any>) {
	window.addEventListener("hashchange", function () {
		locationHashChange(route)
	})
	locationHashChange(route)
}
//////////////////////////////////////////////////////////////
const default404FactoryRouter = createRouter(function (me, router) {
	return dom({
		type: "div",
		style: {
			display: "flex", 'flex-direction': "column", 'align-items': 'center', 'justify-content': 'center'
		},
		children: [
			dom({
				type: "div",
				text: "404"
			}),
			dom({
				type: "div",
				text() {
					return router.param().path.join('/')
				}
			}),
			dom({
				type: "div",
				text() {
					return JSON.stringify(router.param().query)
				}
			})
		]
	})
})

export class QueryWrapper<T extends QueryParam = {}>{
	constructor(public value: T) { }
}

export type FlatRouter<T> = FlatTree<T, QueryWrapper, '/'>